import * as _ from "lodash"
import httpClient from "../httpclient"
import { User, UserServiceV1 } from "../identity"


export * from "./token"

httpClient.interceptors.request.use((config) => {
  const authorizationHeader = config.headers["Authorization"]
  if (!authorizationHeader) {
    const token = SecurityUserHolder.getToken()
    if (token) {
      config.headers["Authorization"] = `${token.tokenType} ${token.accessToken}`
    }
  }
  return config
})


interface AccessTokenType {
  accessToken: string
  tokenType: string
}

class AccessToken {
  private access_token: string
  private token_type: string

  public get accessToken() {
    return this.access_token
  }

  public set accessToken(token: string) {
    this.access_token = token
  }

  public get tokenType() {
    return this.token_type
  }

  public set tokenType(type: string) {
    this.token_type = type
  }
}

export enum GrantType {
  Captcha = "captcha", MobilePassword = "mobile_password", UsernamePassword = "username_password"
}

export class Credentials {
  protected grant_type: GrantType

  public get grantType() {
    return this.grant_type
  }

  public set grantType(grantType) {
    this.grant_type = grantType
  }
}

export class UsernamePasswordCredentials extends Credentials {
  protected grant_type = GrantType.UsernamePassword
  public username: string
  public password: string
}

export class MobilePasswordCredentials extends Credentials {
  protected grant_type = GrantType.MobilePassword
  private country_code: string
  public mobile: string
  public password: string

  public get countryCode() {
    return this.country_code
  }

  public set countryCode(countryCode) {
    this.country_code = countryCode
  }
}

export class CaptchaCredentials extends Credentials {
  protected grant_type = GrantType.Captcha
  public code: string
  public token: string
}

export class AuthenticationService {

  public static authenticate(credentials: Credentials): Promise<AccessToken> {
    return httpClient.post(`/v1/token`, {}, { params: credentials })
      .then(({ data }) => _.assign(new AccessToken(), data))
  }
}

/**
 * Security user holder, Holds the current logged-in user.
 */
export class SecurityUserHolder {

  public static isAuthenticated(): boolean {
    return !_.isEmpty(this.getUser())
  }

  public static logout() {
    localStorage.removeItem("current_user")
  }

  public static authenticate(credentials: Credentials): Promise<AccessToken> {
    return AuthenticationService.authenticate(credentials)
      .then(token => {
        SecurityUserHolder.setToken(token)
        return token
      })
      .then(token => new Promise((resolve, reject) => {
        UserServiceV1.getCurrentUser().then(user => {
          SecurityUserHolder.setUser(user)
          resolve(token)
        }).catch(reject)
      }))
  }

  public static setToken(accessToken: AccessTokenType) {
    localStorage.setItem("access_token", JSON.stringify(_.assign(new AccessToken(), accessToken)))
  }

  public static getToken(): AccessTokenType | undefined {
    const token = localStorage.getItem("access_token")
    return token === null ? undefined : _.assign(new AccessToken(), JSON.parse(token))
  }

  public static setUser(user: User) {
    localStorage.setItem("current_user", JSON.stringify(_.assign(new User(), user)))
  }

  public static getUser(): User {
    const user = localStorage.getItem("current_user")
    return user === null ? undefined : _.assign(new User(), JSON.parse(user))
  }

  public static getUserId(): string {
    const user = this.getUser()
    return _.isUndefined(user) ? "" : user.id
  }
}

export class SubjectHolder {

  public static authenticate = SecurityUserHolder.authenticate

  public static getUserId = SecurityUserHolder.getUserId()

  public static signOut(): Promise<void> {
    return new Promise<void>(resolve => {
      localStorage.removeItem("current_user")
      resolve()
    })
  }
}
